package cluster

import (
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis"
	"github.com/gogo/protobuf/proto"
	"leaf/core/cache"
	"leaf/core/conf"
	"leaf/core/log"
	"leaf/core/rpc"
	"leaf/core/tool"
	"time"
)

func Init() {
	// 初始化本地status
	status.RpcAddr = conf.Config.RpcAddr
	status.ServerId = conf.Config.ServerId
	status.ServerType = conf.Config.ServerType
	status.ServerTypeStr = conf.Config.ServerTypeStr

	// 服务发现线程
	var timer = time.NewTicker(5 * time.Second)
	tool.Go(func() {
		for {
			select {
			case <-tool.GoSigChan():
				Clear()
				log.Release("service close sig")
				return
			case <-timer.C:
				Update()
			}
		}
	})
}

func Clear() {
	var cache = cache.GetInstance()
	if cache == nil {
		log.Error("cluster clear error: cache is nil")
		return
	}

	cache.HDel(fmt.Sprintf("server.info.%v", conf.Config.LogHeader), fmt.Sprintf("%v", status.ServerId))

}

func Update() {
	var cache = cache.GetInstance()
	if cache == nil {
		log.Error("cluster update error: cache is nil")
		return
	}

	for _, stype := range conf.Config.RpcServers {
		var infos, err = cache.HGetAll(fmt.Sprintf("server.info.%v", stype)).Result()
		if err != nil && err != redis.Nil {
			log.Error("cluster update error:%v", err.Error())
			return
		}

		for _, val := range infos {
			var s = NodeStatus{}
			if err = json.Unmarshal([]byte(val), &s); err != nil {
				log.Error("discovery unmarshal err:%v", err.Error())
				continue
			}

			if s.ServerId == status.ServerId {
				continue
			}

			nodesMgr.AddOrUpdateNode(&s)
		}
	}

	if data, err := json.Marshal(status.NodeStatus); err != nil {
		log.Error("%v", err.Error())
	} else {
		cache.HSet(fmt.Sprintf("server.info.%v", conf.Config.LogHeader), fmt.Sprintf("%v", status.ServerId), data)
	}
}

var rpcServer *rpc.RpcServer

func Start() {
	if conf.Config.RpcAddr != "" {
		rpcServer = rpc.StartServer(conf.Config.RpcAddr)
		rpcServer.Start(true)
	}
}

func Destroy() {
	tool.Try(func() {
		rpcServer.Close()
		nodesMgr.Destroy()
	}, nil)
}

func GetNode(sid int32) *Node {
	return nodesMgr.GetNode(sid)
}

func GetNodes(stype int32) []*Node {
	return nodesMgr.GetNodes(stype)
}

func GetBestNode(stype int32) *Node {
	return nodesMgr.GetRandNode(stype)
}

func BroadCast(stype int32, method string, request proto.Message) {
	ns := GetNodes(stype)
	for _, node := range ns {
		node.Cast(method, request)
	}
}
